线程 状态管理

  1. BLOCKED
  2. WAITING
    1. wait
    2. join
  3. TIMED_WAITING
    1. sleep
    2. join
    3. wait
  4. 守护线程的创建和运行
  5. ThreadUtil

从[Oracle官方文档](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.State.html)中我们知道Java线程有如下几种状态
  • NEW : Thread对象已经创建出来但是还没有运行.
  • RUNNABLE : 线程正在执行.
  • BLOCKED : 线程等待锁
  • WAITING : 线程正在等待另一个线程来对自己执行某个特定的行为.
  • TIMED_WAITING: 线程正在等待(在超时范围内)另一个线程来对自己执行某个特定的行为.
  • TERMINATED : 线程退出.

从google上找了俩张线程图

我们重点看一下BLOCKED, WAITING, TIMED_WAITING

BLOCKED

当等待锁的时候,会引发线程的等待状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TestLock {

private static final Object lock = new Object();

public static void main(String[] args) {
ThreadUtil.printThreadState("thread1", "thread2");

Runnable runnable = () -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " Sleep");
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 5000) {
break;
}
}
System.out.println(Thread.currentThread().getName() + " Sleep Finshed");
}
};
Thread thread1 = new Thread(runnable, "thread1");
Thread thread2 = new Thread(runnable, "thread2");
thread1.start();
thread2.start();
}

}

结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
thread1 Sleep
2016-09-22T17:05:37.064 thread2 -> BLOCKED
2016-09-22T17:05:37.064 thread1 -> RUNNABLE
2016-09-22T17:05:38.029 thread2 -> BLOCKED
2016-09-22T17:05:38.029 thread1 -> RUNNABLE
2016-09-22T17:05:39.030 thread2 -> BLOCKED
2016-09-22T17:05:39.030 thread1 -> RUNNABLE
2016-09-22T17:05:40.031 thread2 -> BLOCKED
2016-09-22T17:05:40.031 thread1 -> RUNNABLE
thread1 Sleep Finshed
thread2 Sleep
2016-09-22T17:05:41.032 thread2 -> RUNNABLE
2016-09-22T17:05:42.033 thread2 -> RUNNABLE
2016-09-22T17:05:43.034 thread2 -> RUNNABLE
2016-09-22T17:05:44.035 thread2 -> RUNNABLE
2016-09-22T17:05:45.037 thread2 -> RUNNABLE
thread2 Sleep Finshed

我们看到thread2就进入了BLOCKED状态.

WAITING

调用下面三个方法,线程会进入WAITING状态

  • Object.wait with no timeout
  • Thread.join with no timeout
  • LockSupport.park

注意在调用 Object.waitThread.join 方法时, 不能传参, 否则线程不会进入到WAITING状态, 我在join中做了个实现

wait

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class TestWait {
private static final Object lock = new Object();

public static void main(String[] args) throws InterruptedException {
ThreadUtil.printThreadState("User");

Thread thread = new Thread(() -> {
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 2000) {
break;
}

}
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "User");
thread.start();

Thread notify = new Thread(() -> {
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 5000) {
break;
}

}
synchronized (lock) {
lock.notify();
}
}, "notify");
notify.start();
}
}

结果为

1
2
3
4
5
2016-09-22T17:18:12.745  User -> RUNNABLE
2016-09-22T17:18:13.747 User -> WAITING
2016-09-22T17:18:14.729 User -> WAITING
2016-09-22T17:18:15.712 User -> WAITING
2016-09-22T17:18:16.713 User -> WAITING

join

Threead类的join()方法被调用时,调用它的线程将被挂起,直到这个线程对象完成它的任务join(long millseconds)如果使用这种方法,被挂起的线程只要满足指定的毫秒数到,或者join线程运行完,被挂起线程恢复运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import java.time.LocalDateTime;

public class TestJoin {

public static void main(String[] args) throws InterruptedException {
ThreadUtil.printThreadState("main", "car", "dog");

Thread carThread = new Thread(() -> {
System.out.println(LocalDateTime.now() + " Car run");
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 3000) {
break;
}
}
System.out.println(LocalDateTime.now() + " Car Stop");
}, "car");

Thread dogThread = new Thread(() -> {
System.out.println(LocalDateTime.now() + " Dog run");
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 3000) {
break;
}
}
System.out.println(LocalDateTime.now() + " Dog Stop");
}, "dog");

try {
dogThread.start();
carThread.start();
System.out.println("Begin join");
carThread.join();
System.out.println("Started join");
} catch (Exception e) {
e.printStackTrace();
}
}
}

结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Begin join
2016-09-22T17:17:04.053 Car run
2016-09-22T17:17:04.053 Dog run
2016-09-22T17:17:05.011 car -> RUNNABLE
2016-09-22T17:17:05.011 main -> WAITING
2016-09-22T17:17:05.011 dog -> RUNNABLE
2016-09-22T17:17:06.013 car -> RUNNABLE
2016-09-22T17:17:06.013 main -> WAITING
2016-09-22T17:17:06.013 dog -> RUNNABLE
2016-09-22T17:17:07.011 car -> RUNNABLE
2016-09-22T17:17:07.011 main -> WAITING
2016-09-22T17:17:07.011 dog -> RUNNABLE
2016-09-22T17:17:07.055 Car Stop
Started join
2016-09-22T17:17:07.060 Dog Stop

当我调用carThread.join(3);结果为

1
2
3
4
5
6
7
8
9
10
11
12
Begin join
Started join
2016-09-22T17:19:26.029 Car run
2016-09-22T17:19:26.036 Dog run
2016-09-22T17:19:27.004 dog -> RUNNABLE
2016-09-22T17:19:27.005 car -> RUNNABLE
2016-09-22T17:19:28.004 dog -> RUNNABLE
2016-09-22T17:19:28.004 car -> RUNNABLE
2016-09-22T17:19:29.005 dog -> RUNNABLE
2016-09-22T17:19:29.005 car -> RUNNABLE
2016-09-22T17:19:29.030 Car Stop
2016-09-22T17:19:29.037 Dog Stop

TIMED_WAITING

调用下列方法线程会进入TIMED_WAITING状态

  • Thread.sleep
  • Object.wait with timeout
  • Thread.join with timeout
  • LockSupport.parkNanos
  • LockSupport.parkUntil

sleep

使用sleep()方法中断线程的运行. sleep()中断线程后,直到CPU时钟来临JVM选中它继续执行的这段期间, 该线程不会占用任何资源

yield()方法通知JVM该线程对象可以释放CPU了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.concurrent.TimeUnit;

public class TestSleep {

public static void main(String[] args) {
ThreadUtil.printThreadState("User");

Thread thread = new Thread(() -> {
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 2000) {
break;
}
}
System.out.println("User Sleep");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println("User Sleep Finshed");
}, "User");
thread.start();
}
}

结果为

1
2
3
4
5
6
2016-09-22T17:29:12.378  User -> RUNNABLE
2016-09-22T17:29:13.345 User -> RUNNABLE
User Sleep
2016-09-22T17:29:14.346 User -> TIMED_WAITING
2016-09-22T17:29:15.347 User -> TIMED_WAITING
User Sleep Finshed

join

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.time.LocalDateTime;

public class TestJoin {

public static void main(String[] args) throws InterruptedException {
ThreadUtil.printThreadState("main", "car", "dog");

Thread carThread = new Thread(() -> {
System.out.println(LocalDateTime.now() + " Car run");
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 3000) {
break;
}
}
System.out.println(LocalDateTime.now() + " Car Stop");
}, "car");

Thread dogThread = new Thread(() -> {
System.out.println(LocalDateTime.now() + " Dog run");
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 3000) {
break;
}
}
System.out.println(LocalDateTime.now() + " Dog Stop");
}, "dog");

try {
dogThread.start();
carThread.start();

System.out.println("Begin join");
carThread.join(3000);
System.out.println("end join");
} catch (Exception e) {
e.printStackTrace();
}
}
}

结果为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Begin join
2016-09-22T18:23:56.591 Dog run
2016-09-22T18:23:56.601 Car run
2016-09-22T18:23:57.646 main -> TIMED_WAITING
2016-09-22T18:23:57.647 dog -> RUNNABLE
2016-09-22T18:23:57.647 car -> RUNNABLE
2016-09-22T18:23:58.582 main -> TIMED_WAITING
2016-09-22T18:23:58.582 dog -> RUNNABLE
2016-09-22T18:23:58.582 car -> RUNNABLE
end join
2016-09-22T18:23:59.583 dog -> RUNNABLE
2016-09-22T18:23:59.583 car -> RUNNABLE
2016-09-22T18:23:59.592 Dog Stop
2016-09-22T18:23:59.602 Car Stop

wait

参考join

守护线程的创建和运行

守护线程的优先级很低,通常来说,同一个应用程序中没有其他的线程运行,守护线程才运行. 当守护线程运行结束后,JVM也就结束了这个应用程序

守护线程通常用来作为同一程序中普通线程的服务提供者,因为没有办法确定守护线程什么时候才能获取CPU时钟, 而且在没有其他线程运行的时候,守护线程随时可能会结束

一个典型的守护线程就是java的垃圾回收器

setDeamon()方法只能在start()方法之前被调用,一旦线程开始运行,将不能再修改其状态

注: 需要注意的是,只有在没有用户线程运行的时候,而不是没有用户线程存在的时候守护线程才运行. 例如当所有用户线程多沉睡后,也会被视为没有用户线程执行

  1. Thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
  2. 在Daemon线程中产生的新线程也是Daemon的
  3. 不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了。
  4. 当用户线程都运行完后,守护线程也就跟着结束了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Main {
public static void main(String[] args) {
DeameanThread dt = new DeameanThread();
NormalThread nt = new NormalThread();
nt.start();
dt.start();
System.out.println("main");
}
}

class NormalThread extends Thread {
NormalThread() {
setDaemon(false);
}
@Override
public void run() {
long old = System.currentTimeMillis();
while((System.currentTimeMillis() - old) < 3000) {}
System.out.println("NormalThread");
}
}

class DeameanThread extends Thread {
DeameanThread() {
setDaemon(true);
}
@Override
public void run() {
while(true){}
}
}

ThreadUtil

在上面的例子中用到的输出线程状态信息的工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.time.LocalDateTime;

public class ThreadUtil {

public static void printThreadState(String... filter) {
Thread print = new Thread(() -> {
long now = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - now > 1000) {
now = System.currentTimeMillis();
Thread.getAllStackTraces().forEach((key, thread) -> {
for (int i = 0; i < filter.length; i++) {
if (key.getName().equals(filter[i])) {
System.out.println(LocalDateTime.now() + " " +key.getName() + " -> " + key.getState());
}
}
});
}
}
}, "Print");
print.start();
}
}